# -*- coding: utf-8 -*-
import sys

import geopandas as gpd
from shapely.geometry import Point
import math
import pandas as pd

# ================== 配置（请根据实际修改路径） ==================
excelFileName = "分界镇"
in_fc = rf"D:\Data\完成\{excelFileName}.shp"
out_excel = rf"D:\data\{excelFileName}起止点、走向.xlsx"
name_field = "标准名称"                 # 你的道路名称字段
tol = 0.5                              # 端点匹配缓冲（单位视坐标系而定，米制可用 0.1~1）
perp_tol_deg = 20                      # 垂直优先时允许的角度差（度）
# =============================================================

gdf = gpd.read_file(in_fc)
if name_field not in gdf.columns:
    raise ValueError(f"未找到字段 '{name_field}'，请确认字段名是否正确。")

# 计算道路主方向（按每段长度加权）
def main_direction(line):
    coords = list(line.coords)
    total_dx = 0.0
    total_dy = 0.0
    total_len = 0.0
    for i in range(len(coords)-1):
        x1, y1 = coords[i]
        x2, y2 = coords[i+1]
        dx = x2 - x1
        dy = y2 - y1
        seg_len = math.hypot(dx, dy)
        if seg_len == 0:
            continue
        total_dx += dx * seg_len
        total_dy += dy * seg_len
        total_len += seg_len
    if total_len == 0:
        # 退化情况，使用首尾点
        x1, y1 = coords[0]
        x2, y2 = coords[-1]
        angle = math.degrees(math.atan2(y2 - y1, x2 - x1)) % 360
        return angle
    angle = math.degrees(math.atan2(total_dy, total_dx)) % 360
    return angle

# 根据角度分为 4 类（东西 / 南北 / 东北-西南 / 东南-西北）
def azimuth_to_category(angle):
    angle = angle % 360
    if (angle >= 337.5 or angle < 22.5) or (157.5 <= angle < 202.5):
        return "东西"
    if (67.5 <= angle < 112.5) or (247.5 <= angle < 292.5):
        return "南北"
    if (22.5 <= angle < 67.5) or (202.5 <= angle < 247.5):
        return "东北-西南"
    if (112.5 <= angle < 157.5) or (292.5 <= angle < 337.5):
        return "东南-西北"
    return "东西"

# 根据类别判断哪端是 label1（起侧）/ label2（止侧）
def pick_endpoints_coords(p1, p2, category):
    x1, y1 = p1
    x2, y2 = p2
    if category == "东西":
        if x1 >= x2:
            return (p1, p2)  # (东端, 西端)
        else:
            return (p2, p1)
    elif category == "南北":
        if y1 <= y2:
            return (p1, p2)  # (南端, 北端)
        else:
            return (p2, p1)
    elif category == "东北-西南":
        # 越偏东北，x+y 越大
        if (x1 + y1) >= (x2 + y2):
            return (p1, p2)  # (东北, 西南)
        else:
            return (p2, p1)
    elif category == "东南-西北":
        # 越偏东南, x - y 越大
        if (x1 - y1) >= (x2 - y2):
            return (p1, p2)  # (东南, 西北)
        else:
            return (p2, p1)
    else:
        # 兜底按 x 比
        if x1 >= x2:
            return (p1, p2)
        else:
            return (p2, p1)

# 在点位寻找邻接道路：优先选择与当前道路近似垂直的相交道路
def find_neighbor_at_point(pt, gdf, idx_self, current_angle, tol, perp_tol_deg):
    # 使用空间索引筛选候选
    candidates = gdf
    try:
        sidx = gdf.sindex
        possible_idx = list(sidx.intersection(pt.buffer(tol).bounds))
        if possible_idx:
            candidates = gdf.iloc[possible_idx]
        else:
            # 若空，尝试更大缓冲
            possible_idx = list(sidx.intersection(pt.buffer(max(tol, 1.0)).bounds))
            if possible_idx:
                candidates = gdf.iloc[possible_idx]
    except Exception:
        # 若没有空间索引或失败，则用全部
        candidates = gdf

    # 排除自身
    candidates = candidates[candidates.index != idx_self]
    if candidates.empty:
        return None

    # 先找几何相交的
    inter = candidates[candidates.geometry.intersects(pt.buffer(tol))]
    if not inter.empty:
        # 计算每个候选线的主方向，与当前道路角度的差距（以 90° 为目标）
        score_list = []
        for jdx, crow in inter.iterrows():
            try:
                ang = main_direction(crow.geometry)
            except Exception:
                ang = main_direction(crow.geometry) if hasattr(crow.geometry, "coords") else None
            if ang is None:
                diff = 180.0
            else:
                # 计算与当前线夹角距离 90° 的偏差
                diff = abs(((ang - current_angle + 360) % 180) - 90)
            score_list.append((jdx, crow[name_field], diff))
        # 按 diff 排序，优先取 diff 最小且小于阈值的（即近似垂直）
        score_list.sort(key=lambda x: x[2])
        if score_list and score_list[0][2] <= perp_tol_deg:
            return str(score_list[0][1])
        # 若没有近似垂直的，直接返回第一个相交的道路名（可改为距离最小）
        return str(inter.iloc[0][name_field])

    # 若没有几何相交，再按距离阈值找最近的
    dists = candidates.copy()
    try:
        dists["dist"] = dists.geometry.distance(pt)
        dists = dists.sort_values("dist")
        if not dists.empty and dists.iloc[0]["dist"] <= tol * 10:  # 允许范围放大一些
            return str(dists.iloc[0][name_field])
    except Exception:
        pass

    return None

# 构建最终描述（并处理“反过来起止/路尾”的规则）
def build_description(category, label1_name, label2_name):
    label_map = {
        "东西": ("东", "西"),
        "南北": ("南", "北"),
        "东北-西南": ("东北", "西南"),
        "东南-西北": ("东南", "西北")
    }
    start_label, end_label = label_map.get(category, ("东", "西"))

    n1 = label1_name  # 对应 start_label 侧的邻接路名或 None
    n2 = label2_name  # 对应 end_label 侧的邻接路名或 None

    # 两端都有
    if n1 and n2:
        return f"{start_label}起{n1}；{end_label}止{n2}"
    # start 有， end 无
    if n1 and not n2:
        return f"{start_label}起{n1}；{end_label}止路尾"
    # start 无， end 有 -> 反转规则：起点用有路的一侧为“起”，另一侧写“路尾”
    if not n1 and n2:
        return f"{end_label}起{n2}；{start_label}止路尾"
    # 都无
    return f"{start_label}起空地；{end_label}止空地"

# ========== 主处理 ==========
rows = []
for idx, row in gdf.iterrows():
    geom_line = row.geometry
    road_nm = str(row[name_field])
    if geom_line is None:
        print(f"警告：道路 {row[name_field]} 的几何为空，已跳过")
        continue  # 跳过这一条
    coords = list(geom_line.coords)

    pA = coords[0]
    pB = coords[-1]

    angle = main_direction(geom_line)                   # 主方向角度
    category = azimuth_to_category(angle)               # 类别
    label1_pt, label2_pt = pick_endpoints_coords(pA, pB, category)
    pt1 = Point(label1_pt)
    pt2 = Point(label2_pt)

    # 在 label1（起侧）和 label2（止侧）点寻找邻接道路
    neigh1 = find_neighbor_at_point(pt1, gdf, idx, angle, tol, perp_tol_deg)
    neigh2 = find_neighbor_at_point(pt2, gdf, idx, angle, tol, perp_tol_deg)

    desc = build_description(category, neigh1, neigh2)

    rows.append({
        "道路本身": road_nm,
        "主方向角度": round(angle, 2),
        "走向类别": category,
        "起侧点邻接道路": neigh1 if neigh1 else "空地",
        "止侧点邻接道路": neigh2 if neigh2 else "空地",
        "起止描述": desc
    })

df = pd.DataFrame(rows)
df.to_excel(out_excel, index=False, engine="openpyxl")
print("已生成：", out_excel)
sys.exit(0)